/**
  * @file    RFID_RC522_Config.c
  * @brief   RFID_RC522 利用IO模拟SPI时序实现读取写入信息
  * @note    硬件依赖（GPIO模拟SPI）：
  *         1--SDA  <----->PA4   (片选CS)
  *         2--SCK  <----->PA5   (时钟线)
  *         3--MOSI <----->PA7   (主机输出从机输入)
  *         4--MISO <----->PA6   (主机输入从机输出)
  *         5--悬空
  *         6--GND  <----->GND
  *         7--RST  <----->PB0   (复位引脚)
  *         8--VCC  <----->VCC
  * @note    M1卡结构：
  *         - 16个扇区，每扇区4块（块0-3），绝对地址0~63。
  *         - 块0为厂商代码（只读），块1-2为数据块，块3为控制块（密码/存取控制）。
  */

#include "RFID_RC522_Config.h"

//
// M1卡分为16个扇区，每个扇区由四个块（块0、块1、块2、块3）组成
// 将16个扇区的64个块按绝对地址编号为：0~63
// 第0个扇区的块0（即绝对地址0块），用于存放厂商代码，已经固化不可更改 
// 每个扇区的块0、块1、块2为数据块，可用于存放数据
// 每个扇区的块3为控制块（绝对地址为:块3、块7、块11.....）包括密码A，存取控制、密码B等

/*全局变量*/
u8 CT[2];           // 卡类型（Card Type）
u8 SN[4];           // 卡序列号（Serial Number）
u8 RFID[16];        // 存储读取的RFID数据
u8 lxl_bit=0;       // 标志位：卡0是否识别（示例卡）
u8 card1_bit=0;     // 标志位：卡1是否识别
u8 card2_bit=0;     // 标志位：卡2是否识别
u8 card3_bit=0;     // 标志位：卡3是否识别
u8 card4_bit=0;     // 标志位：卡4是否识别
u8 total=0;         // 总识别卡数（未使用）

// 预定义的卡序列号（用于匹配）
u8 lxl[4]={196,58,104,217};        // 卡0的UID
u8 card_1[4]={83,106,11,1};        // 卡1的UID
u8 card_2[4]={208,121,31,57};      // 卡2的UID
u8 card_3[4]={176,177,143,165};    // 卡3的UID
u8 card_4[4]={5,158,10,136};       // 卡4的UID

u8 KEY[6]={0xff,0xff,0xff,0xff,0xff,0xff}; // 默认密钥（全FF）
u8 AUDIO_OPEN[6] = {0xAA, 0x07, 0x02, 0x00, 0x09, 0xBC}; // 音频控制指令（未使用）
u8 RFID1[16]={0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x07,
			  0x80,0x29,0xff,0xff,0xff,0xff,0xff,0xff};
/*函数声明*/
u8 status;
u8 s=0x08;

/**
  ******************************************************************************
  * @brief  RC522射频卡主处理函数
  * @param  None
  * @retval None
  * @note   执行流程：
  *         1. 寻卡 → 2.防冲突 → 3.UID匹配 → 4.选卡 → 5.验证 → 6.读数据
  *         典型处理时间：约100ms（取决于卡片响应速度）
  ******************************************************************************
  */
void RC522_Handel(void)
{
    /*---------------- 第一阶段：卡片检测 ----------------*/
    /* 发送REQALL指令寻找范围内所有符合ISO14443A标准的卡片 */
    status = PcdRequest(PICC_REQALL, CT);  // CT存储返回的卡片类型码
    
    // 调试用状态输出（正式发布时可注释掉）
    //USART1_Printf("\r\nstatus>>>>>>%d\r\n", status);
    
    /* 寻卡成功时进入防冲突流程 */
    if(status == MI_OK) 
    {
        status = MI_ERR;  // 重置状态标志
        /* 执行防冲突协议获取卡片唯一标识符UID */
        status = PcdAnticoll(SN);  // SN数组存储4字节UID	       
    }

    /*---------------- 第二阶段：UID处理 ----------------*/
    if (status == MI_OK) // 防冲突成功
    {
        status = MI_ERR;  // 重置状态标志
        
        /* 通过串口输出卡片UID（十六进制格式） */
        USART1_Printf("Card UID: %02X %02X %02X %02X\n", 
                     SN[0], SN[1], SN[2], SN[3]);
        
        /* 卡片白名单匹配系统（可扩展为数组遍历方式）*/
        // 匹配预设卡片1（示例：lxl卡）
        if((SN[0]==lxl[0]) && (SN[1]==lxl[1]) &&
           (SN[2]==lxl[2]) && (SN[3]==lxl[3])) 
        {
            lxl_bit = 1;  // 设置识别标志位
            USART1_Printf("\r\nThe User is:card_0\r\n"); // 身份识别输出
        }
        // 匹配预设卡片2（工程卡）
        if((SN[0]==card_1[0]) && (SN[1]==card_1[1]) &&
           (SN[2]==card_1[2]) && (SN[3]==card_1[3])) 
        {
            card1_bit = 1;
            USART1_Printf("\r\nThe User is:card_1\r\n");
        }
        // 匹配预设卡片3（访客卡）
        if((SN[0]==card_2[0]) && (SN[1]==card_2[1]) &&
           (SN[2]==card_2[2]) && (SN[3]==card_2[3])) 
        {
            card2_bit = 1;
            USART1_Printf("\r\nThe User is:card_2\r\n");
        }
        // 其他卡片匹配逻辑...
        
        /* 统计识别到的卡片数量（当前注释状态） */
        //total = card1_bit + card2_bit + card3_bit + card4_bit + lxl_bit;
        
        /* 通过UID选择特定卡片 */
        status = PcdSelect(SN);
    }
    else
    {
       /* 寻卡/防冲突失败处理区域 */
       // TODO: 可添加失败重试机制或错误指示灯
    }

    /*---------------- 第三阶段：卡片操作 ----------------*/
    /* 选卡成功后进行密钥验证 */
    if(status == MI_OK) 
    {
        status = MI_ERR;
        /* 验证第9扇区的密钥A（0x60）*/
        status = PcdAuthState(0x60, 0x09, KEY, SN);
    }
    
    /* 验证成功后读取卡片数据 */
    if(status == MI_OK) 
    {
        status = MI_ERR;
        /* 读取指定块数据（s=块号，RFID存储返回数据）*/
        status = PcdRead(s, RFID);
    }
    
    /* 读卡成功后的延时处理 */
    if(status == MI_OK) 
    {
        status = MI_ERR;
        /* 防止重复读取的冷却延时 */
        Delay_ms(100);  // 可根据实际需求调整延时时间
    }	
}

/**
  ******************************************************************************
  * @brief  RC522射频模块初始化函数
  * @param  None
  * @retval None
  * @note   执行流程：
  *         1. 初始化SPI硬件接口
  *         2. 配置RC522复位引脚
  *         3. 执行芯片复位
  *         4. 设置ISO14443A工作模式
  *         典型初始化时间：约5ms
  ******************************************************************************
  */
void RC522_Init(void)
{
    /* 1. 初始化硬件SPI接口 */
    SPI1_Init();
    
    /* 2. 释放复位引脚（PB0置高电平）*/
    RC522_Reset_Disable();
    
    /* 3. 禁用片选信号（PA4置高电平）*/
    RC522_CS_Disable();
    
    /* 4. 执行芯片软复位 */
    PcdReset();
    
    /* 5. 配置为ISO14443A模式 */
    M500PcdConfigISOType('A'); // 参数'A'表示ISO14443A协议
}

/**
  ******************************************************************************
  * @brief  SPI1接口硬件初始化函数
  * @param  None
  * @retval None
  * @note   执行流程：
  *         1. 使能GPIO时钟
  *         2. 配置SPI相关引脚：
  *            - CS(PA4), SCK(PA5), MOSI(PA7) 推挽输出
  *            - MISO(PA6) 浮空输入
  *            - RST(PB0) 推挽输出
  *         注意：本实现使用GPIO模拟SPI时序
  ******************************************************************************
  */
void SPI1_Init(void)
{
    /* 1. 使能GPIOA/GPIOB时钟 */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE);

    GPIO_InitTypeDef GPIO_InitStructure;

    /* 2. 配置SPI控制线（推挽输出50MHz）*/
    // 片选CS(PA4)、时钟SCK(PA5)、主机输出MOSI(PA7)
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;      // 推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;     // 高速模式
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    /* 3. 配置MISO输入线（浮空输入）*/
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;              // 主机输入MISO(PA6)
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 浮空输入模式
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    /* 4. 配置复位引脚RST（推挽输出）*/
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;            // 复位信号PB0
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;      // 推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
}

/**
  ******************************************************************************
  * @brief  通过GPIO模拟SPI发送单字节数据
  * @param  byte: 要发送的数据（8位）
  * @retval None
  * @note   执行流程：
  *         1. 高位(MSB)优先发送
  *         2. 时钟下降沿输出数据
  *         3. 时钟上升沿保持稳定
  *         时序图：
  *         MOSI:   D7   D6   D5   D4   D3   D2   D1   D0
  *         SCK:  _|‾|_|‾|_|‾|_|‾|_|‾|_|‾|_|‾|_|‾|_
  *         CS:   保持低电平
  ******************************************************************************
  */
void SPI_RC522_SendByte(u8 byte)
{
    u8 counter;
    for(counter = 0; counter < 8; counter++)
    {     
        /* 1. 准备数据位（MSB first）*/
        if (byte & 0x80)
            RC522_MOSI_1();  // 输出逻辑1
        else 
            RC522_MOSI_0();  // 输出逻辑0
        
        RC522_Delay();       // 数据建立时间（典型值5us）
        
        /* 2. 产生时钟下降沿（数据采样）*/
        RC522_SCK_0();       // 时钟拉低
        RC522_Delay();       // 数据保持时间
        
        /* 3. 产生时钟上升沿 */
        RC522_SCK_1();       // 时钟拉高
        RC522_Delay();       // 时钟高电平保持时间
        
        /* 4. 移位准备下一位 */
        byte <<= 1;          // 左移处理下一位
    } 
}

/**
  ******************************************************************************
  * @brief  通过GPIO模拟SPI接收单字节数据
  * @param  None
  * @retval 接收到的数据（8位）
  * @note   执行流程：
  *         1. 时钟下降沿触发从机输出
  *         2. 时钟上升沿采样数据
  *         3. 低位(LSB)优先接收
  *         时序图：
  *         MISO:   D7   D6   D5   D4   D3   D2   D1   D0
  *         SCK:  _|‾|_|‾|_|‾|_|‾|_|‾|_|‾|_|‾|_|‾|_
  *         CS:   保持低电平
  ******************************************************************************
  */
u8 SPI_RC522_ReadByte(void)
{
    u8 counter;
    u8 SPI_Data = 0;
    
    for(counter = 0; counter < 8; counter++)
    {
        SPI_Data <<= 1;      // 左移接收数据
        
        /* 1. 产生时钟下降沿（触发从机输出）*/
        RC522_SCK_0();       
        RC522_Delay();       // 从机响应时间
        
        /* 2. 采样数据位（上升沿采样）*/
        if (RC522_MISO_GET() == 1)
            SPI_Data |= 0x01; // 设置当前位为1
            
        RC522_Delay();       // 数据保持时间
        
        /* 3. 产生时钟上升沿 */
        RC522_SCK_1();       
        RC522_Delay();       // 时钟高电平保持时间
    }
    return SPI_Data;
}

/* 地址转换规则说明（修正版）：
 * 官方手册原始说明（翻译）：
 * SPI地址字节格式：
 * 读操作：1AAAAAA0
 * 写操作：0AAAAAA0
 * 其中AAAAAA为6位寄存器地址
 * 
 * 原始寄存器地址格式（7位）：
 * [无用位] [A5 A4 A3 A2 A1 A0] 
 *   0        1  1  1  1  1  1   (示例：0x3F)
 * 
 * 实际物理地址只有6位有效（A5-A0），最高位固定为0
 * 
 * SPI通信时的命令格式：
 * 
 * 读操作命令格式（8位）：
 * [1][A5 A4 A3 A2 A1 A0][0] 
 *  ↑                     ↑
 *  读标志位            末位固定0
 * 
 * 写操作命令格式（8位）：
 * [0][A5 A4 A3 A2 A1 A0][0]
 *  ↑                     ↑
 *  写标志位            末位固定0
 * 
 * 转换方法：
 * 1. 左移1位：将6位地址移到高位，低位补0
 *    e.g. 0x1F (01 1111) → 0011 1110
 * 2. 掩码操作：& 0x7E (0111 1110) 确保bit0为0
 * 3. 设置读/写标志：
 *    - 读操作：| 0x80 (1000 0000)
 *    - 写操作：保持bit7为0
 */

/**
  ******************************************************************************
  * @brief  读取RC522寄存器值
  * @param  ucAddress: 寄存器地址（0x00~0x3F）
  * @retval 读取到的寄存器值
  * @note   执行流程：
  *         1. 构造读命令格式（地址转换）
  *         2. 拉低CS片选信号
  *         3. 发送读命令+寄存器地址
  *         4. 读取寄存器值
  *         5. 释放CS片选信号
  *         通信格式：
  *         [MSB] 1 A5 A4 A3 A2 A1 A0 0 [LSB]
  *         其中最高位1表示读操作
  ******************************************************************************
  */
u8 ReadRawRC(u8 ucAddress)
{
    u8 ucAddr, ucReturn;
    
    /* 1. 构造读命令格式：
     * - 左移1位腾出方向位：A5 A4 A3 A2 A1 A0 0 0
     * - 与0x7E掩码：		0 A5 A4 A3 A2 A1 A0 0
     * - 或0x80设置读标志： 1 A5 A4 A3 A2 A1 A0 0
     */
    ucAddr = ((ucAddress << 1) & 0x7E) | 0x80;
    
    /* 2. 启动SPI通信 */
    RC522_CS_Enable();  // 拉低CS
    
    /* 3. 发送读命令 */
    SPI_RC522_SendByte(ucAddr);
    
    /* 4. 读取寄存器值 */
    ucReturn = SPI_RC522_ReadByte();
    
    /* 5. 结束通信 */
    RC522_CS_Disable(); // 释放CS

    return ucReturn;
}

/**
  ******************************************************************************
  * @brief  写入RC522寄存器
  * @param  ucAddress: 寄存器地址（0x00~0x3F）
  * @param  ucValue: 要写入的数据
  * @retval None
  * @note   执行流程：
  *         1. 构造写命令格式（地址转换）
  *         2. 拉低CS片选信号
  *         3. 发送写命令+寄存器地址
  *         4. 发送要写入的数据
  *         5. 释放CS片选信号
  *         通信格式：
  *         [MSB] 0 A5 A4 A3 A2 A1 A0 0 [LSB]
  *         其中最高位0表示写操作
  ******************************************************************************
  */
void WriteRawRC(u8 ucAddress, u8 ucValue)
{
    u8 ucAddr;
    
    /* 1. 构造写命令格式：
     * - 左移1位腾出方向位：A5 A4 A3 A2 A1 A0 0 0
     * - 与0x7E掩码：		0 A5 A4 A3 A2 A1 A0 0
     */
    ucAddr = (ucAddress << 1) & 0x7E;
    
    /* 2. 启动SPI通信 */
    RC522_CS_Enable();  // 拉低CS
    
    /* 3. 发送写命令 */
    SPI_RC522_SendByte(ucAddr);
    
    /* 4. 发送数据 */
    SPI_RC522_SendByte(ucValue);
    
    /* 5. 结束通信 */
    RC522_CS_Disable(); // 释放CS
}

/**
  ******************************************************************************
  * @brief  设置RC522寄存器的指定位掩码
  * @param  ucReg:  目标寄存器地址（0x00~0x3F）
  * @param  ucMask: 位掩码（需要设置的位对应1，保持不变的位对应0）
  * @retval None
  * @note   执行流程：
  *         1. 读取寄存器当前值
  *         2. 将指定位设置为1（不影响其他位）
  *         3. 写回寄存器
  *         技术要点：
  *         - 采用读-改-写(RMW)操作保证原子性
  *         - 适用于控制寄存器位使能/标志位设置
  *         典型应用场景：
  *         - 使能中断标志位
  *         - 启动射频场发射
  ******************************************************************************
  */
void SetBitMask(u8 ucReg, u8 ucMask)  
{
    u8 ucTemp;

    /* 1. 读取寄存器当前值（保证原始状态不被丢失） */
    ucTemp = ReadRawRC(ucReg);
    
    /* 2. 位或操作设置目标位（遵循读-改-写原则）：
     *    - ucTemp | ucMask 会将ucMask中为1的位设为1
     *    - 其他位保持原值不变
     *    示例：
     *    ucTemp = 0x05 (0000 0101)
     *    ucMask = 0x60 (0110 0000) 
     *    结果   = 0x65 (0110 0101)
     */
    WriteRawRC(ucReg, ucTemp | ucMask);
}

/**
  ******************************************************************************
  * @brief  清除RC522寄存器的指定位掩码
  * @param  ucReg:  目标寄存器地址（0x00~0x3F）
  * @param  ucMask: 位掩码（需要清除的位对应1，保持不变的位对应0）
  * @retval None
  * @note   执行流程：
  *         1. 读取寄存器当前值
  *         2. 将指定位清除为0（不影响其他位）
  *         3. 写回寄存器
  *         技术要点：
  *         - 采用读-改-写(RMW)操作保证原子性
  *         - 适用于中断标志清除/功能禁用等场景
  *         典型应用场景：
  *         - 清除中断标志位
  *         - 关闭射频场发射
  ******************************************************************************
  */
void ClearBitMask(u8 ucReg, u8 ucMask)  
{
    u8 ucTemp;

    /* 1. 读取寄存器当前值 */
    ucTemp = ReadRawRC(ucReg);
    
    /* 2. 位与操作清除目标位：
     *    - ~ucMask 取反后，需要清除的位变为0，其他位为1
     *    - 再与原值按位与，实现只清除指定位置0
     *    示例：
     *    ucTemp  = 0xFF (1111 1111)
     *    ucMask  = 0x60 (0110 0000)
     *    ~ucMask = 0x9F (1001 1111)
     *    结果 	  = 0x9F (1001 1111)
     */
    WriteRawRC(ucReg, ucTemp & (~ucMask));
}

/**
  ******************************************************************************
  * @brief  开启RC522天线射频场
  * @param  None
  * @retval None
  * @note   执行流程：
  *         1. 读取TxControlReg当前状态
  *         2. 若bit0和bit1未同时置位，则开启天线驱动
  *         硬件原理：
  *         - 设置TxControlReg的bit0(Tx1RFEn)和bit1(Tx2RFEn)
  *         - 驱动TX1/TX2引脚输出13.56MHz载波
  *         典型应用：
  *         - 卡片检测前必须调用
  *         - 功耗敏感场景需配合PcdAntennaOff使用
  ******************************************************************************
  */
void PcdAntennaOn(void)
{
    u8 uc;
    
    /* 1. 读取发射控制寄存器当前值 */
    uc = ReadRawRC(TxControlReg);
    
    /* 2. 检查天线驱动位（bit0和bit1）是否已启用
     *    0x03 -> 0000 0011（同时检测两个位）
     */
    if (!(uc & 0x03))
    {
        /* 启用天线驱动（避免重复设置降低效率） */
        SetBitMask(TxControlReg, 0x03);
        
        /* 硬件启动延时（手册要求最小50us）*/
        Delay_us(50); 
    }
}

/**
  ******************************************************************************
  * @brief  关闭RC522天线射频场  
  * @param  None
  * @retval None
  * @note   执行流程：
  *         1. 直接清除TxControlReg的bit0和bit1
  *         安全特性：
  *         - 立即停止射频场发射
  *         - 降低系统功耗（静态电流从40mA降至1mA）
  *         使用场景：
  *         - 低功耗模式切换
  *         - 射频场异常时紧急关闭
  ******************************************************************************
  */
void PcdAntennaOff(void)
{
    /* 直接禁用天线驱动（无需状态检查） */
    ClearBitMask(TxControlReg, 0x03);
    
    /* 寄存器写入延时（确保硬件响应）*/
    Delay_us(10);
}

/**
  ******************************************************************************
  * @brief  执行RC522芯片的硬件和软件复位
  * @param  None
  * @retval None
  * @note   执行流程：
  *         1. 硬件复位脉冲（>37.7μs低电平）
  *         2. 发送软复位命令（0x0F）
  *         3. 等待复位完成（Polling CommandReg）
  *         4. 配置基础通信参数：
  *            - ISO14443A模式
  *            - 定时器设置（TReload=30, TPrescaler=62）
  *            - 100% ASK调制
  *         硬件要求：
  *         - RST引脚需接10kΩ上拉电阻
  *         - 复位期间保持SPI通信稳定
  ******************************************************************************
  */
void PcdReset(void)
{
    /*── 第1阶段：硬件复位时序 ──*/
    RC522_Reset_Disable();  // RST引脚置高（准备产生下降沿）
    Delay_us(1);            // 保持高电平至少1μs
    
    RC522_Reset_Enable();   // RST引脚置低（开始复位）
    Delay_us(1);           // 保持低电平>37.7μs（手册最小值）
    
    RC522_Reset_Disable();  // RST引脚恢复高电平
    Delay_us(1);            // 复位完成等待
    
    /*── 第2阶段：软件复位 ──*/
    WriteRawRC(CommandReg, 0x0F);  // 发送软复位命令（0x0F）
    
    /* 等待复位完成（Polling状态寄存器）*/
    while (ReadRawRC(CommandReg) & 0x10) 
    { 
        /* 等待PowerDown位(bit4)清零 
         * 典型等待时间：约50μs @13.56MHz
         */
    }
    Delay_us(1);  // 稳定时间
    
    /*── 第3阶段：基础寄存器配置 ──*/
    /* 1. 设置通信模式（ISO14443A）*/
    WriteRawRC(ModeReg, 0x3D);  
    /* 位域说明：
     * bit7: 保留
     * bit6: 使能CRC
     * bit5-4: 00=ISO14443A
     * bit3-0: 0xD=CRC初始值0x6363
     */
    
    /* 2. 配置定时器（超时约5ms @13.56MHz）*/
    WriteRawRC(TReloadRegL, 30);    // 定时器重载值低字节
    WriteRawRC(TReloadRegH, 0);     // 高字节
    WriteRawRC(TModeReg, 0x8D);     // 定时器模式
    /* TModeReg说明：
     * bit7: 定时器自动重载
     * bit6: 定时器使能  
     * bit5-0: 0x0D=预留值
     */
    
    WriteRawRC(TPrescalerReg, 0x3E); // 预分频值=62
    /* 计算公式：
     * 定时时间 = (TPrescaler+1)/f_crystal * (TReload+1)
     *          = 63/13.56MHz * 31 ≈ 144μs
     */
    
    /* 3. 配置射频调制 */
    WriteRawRC(TxAutoReg, 0x40);    // 100% ASK调制
    /* 位域说明：
     * bit7: 保留
     * bit6: 使能自动控制
     * bit5-0: 调制深度设置
     */
}

/**
  ******************************************************************************
  * @brief  配置RC522的ISO协议类型（目前仅支持14443A）
  * @param  ucType: 协议类型标识（仅支持'A'）
  * @retval None
  * @note   执行流程：
  *         1. 清除加密状态标志
  *         2. 配置通信模式寄存器
  *         3. 设置接收解调参数
  *         4. 调整射频信号参数
  *         5. 初始化定时器设置
  *         6. 启用天线射频场
  *         硬件要求：
  *         - 需在PcdReset()之后调用
  *         - 天线阻抗匹配网络需按13.56MHz设计
  ******************************************************************************
  */
void M500PcdConfigISOType(u8 ucType)
{
    if (ucType == 'A')  // ISO14443A模式配置
    {
        /*── 1. 清除加密状态 ──*/
        ClearBitMask(Status2Reg, 0x08);  // 清除Status2Reg的bit3(MFCrypto1On)
        /* 作用：确保通信数据未加密 */

        /*── 2. 配置通信模式 ──*/
        WriteRawRC(ModeReg, 0x3D);       // 设置模式寄存器
        /* 位域详解：
         * bit7:   保留(0)
         * bit6:   CRC使能(1)
         * bit5-4: 协议类型(00=ISO14443A)
         * bit3-0: CRC初始值(1101=0x6363)
         */

        /*── 3. 设置接收解调参数 ──*/
        WriteRawRC(RxSelReg, 0x86);      // 配置接收灵敏度
        /* 位域详解：
         * bit7:   保留(1)
         * bit6:   接收增益高(0)
         * bit5-4: 保留(00)
         * bit3-0: 最小信号幅值(0110)
         */

        /*── 4. 配置射频参数 ──*/
        WriteRawRC(RFCfgReg, 0x7F);      // 设置射频配置
        /* 位域详解：
         * bit7:   保留(0)
         * bit6:   峰值增益(1)
         * bit5-4: 保留(11)
         * bit3-0: 接收增益(1111=最大)
         */

        /*── 5. 初始化定时器 ──*/
        WriteRawRC(TReloadRegL, 30);     // 定时器重载值低字节
        WriteRawRC(TReloadRegH, 0);      // 定时器重载值高字节
        WriteRawRC(TModeReg, 0x8D);      // 定时器模式配置
        WriteRawRC(TPrescalerReg, 0x3E); // 预分频值设置
        /* 计算公式：
         * 超时时间 = (TReload+1)*(TPrescaler+1)/13.56MHz
         *          = 31*63/13.56 ≈ 144μs
         */

        /*── 6. 启动射频场 ──*/
        Delay_us(2);                     // 寄存器稳定等待
        PcdAntennaOn();                  // 启用天线驱动
    }
    /* 注：当前版本仅支持14443A，可扩展其他协议 */
}

/**
  ******************************************************************************
  * @brief  RC522与卡片的核心通信函数
  * @param  ucCommand: 命令类型（PCD_AUTHENT/PCD_TRANSCEIVE）
  * @param  pInData:   输入数据缓冲区
  * @param  ucInLenByte: 输入数据长度（字节）
  * @param  pOutData:  输出数据缓冲区
  * @param  pOutLenBit: 输出数据位数（bit单位）
  * @retval 操作状态：
  *         MI_OK       - 操作成功
  *         MI_ERR      - 通信错误
  *         MI_NOTAGERR - 卡片无响应
  * @note   执行流程：
  *         1. 配置中断使能寄存器
  *         2. 清空FIFO缓冲区
  *         3. 写入命令和数据到FIFO
  *         4. 启动命令执行
  *         5. 等待操作完成或超时
  *         6. 处理返回数据及状态
  *         安全特性：
  *         - 自动超时控制（25ms）
  *         - 多重错误检测机制
  ******************************************************************************
  */
char PcdComMF522(u8 ucCommand, u8 *pInData, u8 ucInLenByte, 
               	 u8 *pOutData, u32 *pOutLenBit)
{
    /* 通信状态跟踪 */
	char cStatus = MI_ERR; 	// 函数执行结果状态码：
							// - MI_OK(0): 操作成功
							// - MI_ERR(1): 通信错误
							// - MI_NOTAGERR(2): 卡片无响应

	/* 中断控制相关 */
	u8 ucIrqEn = 0x00;  	// 中断使能掩码（按位控制）：
							// bit7: IRqInv - 中断信号反相控制
							// bit6: TimerIEn - 定时器中断使能
							// bit5: LoAlertIEn - 低电平警报中断
							// bit4: ErrIEn - 错误中断
							// bit3: HiAlertIEn - 高电平警报中断
							// bit2: IdleIEn - 空闲中断
							// bit1: RxIEn - 接收中断
							// bit0: TxIEn - 发送中断

	u8 ucWaitFor = 0x00;	// 等待的中断标志掩码：
							// 用于轮询ComIrqReg时的条件判断
							// 各bit定义与ucIrqEn相同

	/* 数据接收处理 */
	u8 ucLastBits;      	// 最后一个字节的有效位数（0-7）：
							// 从ControlReg的bit0-2获取
							// 0表示整个字节有效

	u8 ucN;                // 临时计数器/状态存储：
							// 1. 用于存储FIFO中的字节数
							// 2. 用于中断状态检查
							// 3. 数据拷贝时的循环计数器

	/* 超时控制 */
	u32 ul;             	// 超时计数器（单位：循环次数）：
							// 典型值1000对应约25ms超时（@8MHz）
							// 递减计数至0触发超时处理

    /*── 1. 根据命令类型配置中断参数 ──*/
    switch (ucCommand)
    {
       case PCD_AUTHENT:    // Mifare认证命令
			ucIrqEn   = 0x12; // 使能ErrIEn(bit4)和IdleIEn(bit1)
			ucWaitFor = 0x10; // 等待IdleIRq(bit4)标志
			break;
			 
       case PCD_TRANSCEIVE: // 数据传输命令
			ucIrqEn   = 0x77; // 使能所有关键中断
			ucWaitFor = 0x30; // 等待RxIRq(bit5)和IdleIRq(bit4)
			break; 
       default:
         	break; 
    }
   
    /*── 2. 初始化通信状态 ──*/
    WriteRawRC(ComIEnReg, ucIrqEn | 0x80); // 设置IRqInv(bit7)和中断使能
    ClearBitMask(ComIrqReg, 0x80);         // 清除所有中断标志
    WriteRawRC(CommandReg, PCD_IDLE);      // 写入空闲命令
    SetBitMask(FIFOLevelReg, 0x80);        // 清空FIFO缓冲区

    /*── 3. 写入输入数据到FIFO ──*/
    for (ul = 0; ul < ucInLenByte; ul++)
        WriteRawRC(FIFODataReg, pInData[ul]);

    /*── 4. 启动命令执行 ──*/
    WriteRawRC(CommandReg, ucCommand);
    
    /* 特殊处理：TRANSCEIVE命令需启动发送 */
    if (ucCommand == PCD_TRANSCEIVE)
        SetBitMask(BitFramingReg, 0x80); // 设置StartSend位

    /*── 5. 等待操作完成（25ms超时）──*/
    ul = 1000; // 超时计数器（实际超时时间≈25ms@8MHz）
    do {
        ucN = ReadRawRC(ComIrqReg); // 读取中断状态
        ul--;
    } while ((ul != 0) && 
            !(ucN & 0x01) &&       // TimerIRq(bit0)
            !(ucN & ucWaitFor));   // 等待指定标志
    
    /*── 6. 清理发送状态 ──*/
    ClearBitMask(BitFramingReg, 0x80); // 清除StartSend位

    /*── 7. 处理操作结果 ──*/
    if (ul != 0)  // 未超时
    {
        /* 检查错误寄存器 */
        if (!(ReadRawRC(ErrorReg) & 0x1B)) // 屏蔽BufferOvfl|CollErr|ParityErr|ProtocolErr
        {
            cStatus = MI_OK;
            
            /* 处理定时器中断（卡片无响应） */
            if (ucN & ucIrqEn & 0x01)  
                cStatus = MI_NOTAGERR;   
                
            /* 处理接收数据（仅TRANSCEIVE命令） */
            if ((cStatus == MI_OK) && (ucCommand == PCD_TRANSCEIVE))
            {
                ucN = ReadRawRC(FIFOLevelReg);       // 获取接收字节数
                ucLastBits = ReadRawRC(ControlReg) & 0x07; // 最后字节有效位数
                
                /* 计算总位数 */
                *pOutLenBit = (ucLastBits) ? 
                             ((ucN - 1) * 8 + ucLastBits) : // 非完整字节
                             (ucN * 8);                   // 完整字节
                
                /* 读取数据（限制最大长度） */
                ucN = (ucN == 0) ? 1 : ((ucN > MAXRLEN) ? MAXRLEN : ucN);
                for (ul = 0; ul < ucN; ul++)
                    pOutData[ul] = ReadRawRC(FIFODataReg);
            }
        }
        else
        {
            cStatus = MI_ERR;
            // 调试时可在此打印ErrorReg值
        }
    }
   
    /*── 8. 恢复空闲状态 ──*/
    SetBitMask(ControlReg, 0x80);   // 停止定时器
    WriteRawRC(CommandReg, PCD_IDLE); 
    
    return cStatus;
}

/**
  ******************************************************************************
  * @brief  发送寻卡请求（REQA/WUPA命令）
  * @param  ucReq_code: 请求指令码
  *          - 0x26: REQA（寻卡请求）
  *          - 0x52: WUPA（唤醒请求）
  * @param  pTagType: 卡片类型输出缓冲区（2字节）
  * @retval 操作状态：
  *         MI_OK  - 寻卡成功
  *         MI_ERR - 寻卡失败
  * @note   执行流程：
  *         1. 清理加密状态
  *         2. 配置帧格式寄存器
  *         3. 启用射频场
  *         4. 发送寻卡指令
  *         5. 验证返回数据
  *         硬件要求：
  *         - 天线需已通过PcdAntennaOn()激活
  *         - 典型寻卡时间：1.2ms@13.56MHz
  ******************************************************************************
  */
char PcdRequest(u8 ucReq_code, u8 *pTagType)
{
    char cStatus;  
    u8 ucComMF522Buf[MAXRLEN];  // 通信缓冲区（MAXRLEN=18）
    u32 ulLen;                  // 接收数据位长度

    /*── 1. 初始化通信状态 ──*/
    ClearBitMask(Status2Reg, 0x08);  // 清除Status2Reg的bit3(MFCrypto1On)
                                     // 确保通信未加密
    
    /*── 2. 配置帧格式 ──*/
    WriteRawRC(BitFramingReg, 0x07); // 设置最后字节7位有效
                                     // bit2-0: RxLastBits=7
    
    /*── 3. 启用射频场 ──*/
    SetBitMask(TxControlReg, 0x03);  // 设置TxControlReg的bit0-1
                                     // 使能TX1/TX2引脚载波输出

    /*── 4. 发送寻卡命令 ──*/
    ucComMF522Buf[0] = ucReq_code;   // 装载请求指令
    cStatus = PcdComMF522(PCD_TRANSCEIVE, ucComMF522Buf, 1, 
                         ucComMF522Buf, &ulLen);
    
    /*── 5. 验证响应 ──*/
    if ((cStatus == MI_OK) && (ulLen == 0x10))  // 成功接收16位响应
    {    
       pTagType[0] = ucComMF522Buf[0];  // 卡片类型字节1（ATQA高字节）
       pTagType[1] = ucComMF522Buf[1];  // 卡片类型字节2（ATQA低字节）
    }
    else
    {
       cStatus = MI_ERR;  // 设置错误状态
       /* 调试时可添加错误分析：
        * if(ulLen != 0x10) USART1_Printf("响应长度错误:%lu", ulLen);
        */
    }

    return cStatus;
}

/**
  ******************************************************************************
  * @brief  执行ISO14443A防冲突流程，获取卡片UID
  * @param  pSnr: 卡片UID输出缓冲区（4字节+校验字节）
  * @retval 操作状态：
  *         MI_OK  - 成功获取UID
  *         MI_ERR - 校验失败或通信错误
  * @note   执行流程：
  *         1. 初始化防冲突状态
  *         2. 发送防冲突指令（0x93 0x20）
  *         3. 接收UID及校验字节
  *         4. 验证BCC校验
  *         协议规范：
  *         - 符合ISO14443-3 Type A防冲突流程
  *         - 支持多卡片同时响应处理
  *         硬件要求：
  *         - 射频场需已通过PcdAntennaOn()激活
  ******************************************************************************
  */
char PcdAnticoll(u8 *pSnr)
{
    char cStatus;
    u8 uc, ucSnr_check = 0;  // 校验字节计算临时变量
    u8 ucComMF522Buf[MAXRLEN]; 
    u32 ulLen;

    /*── 1. 初始化防冲突状态 ──*/
    ClearBitMask(Status2Reg, 0x08);  // 清除Status2Reg的bit3(MFCrypto1On)
    WriteRawRC(BitFramingReg, 0x00); // 复位帧格式寄存器
    ClearBitMask(CollReg, 0x80);     // 清除CollReg的bit7(ValuesAfterColl)

    /*── 2. 构建防冲突指令 ──*/
    ucComMF522Buf[0] = 0x93;  // 防冲突指令码（针对UID CL1）
    ucComMF522Buf[1] = 0x20;  // 有效位数为32bit（4字节UID+1字节BCC）

    /*── 3. 执行防冲突通信 ──*/
    cStatus = PcdComMF522(PCD_TRANSCEIVE, ucComMF522Buf, 2, 
                         ucComMF522Buf, &ulLen);

    /*── 4. 处理响应数据 ──*/
    if (cStatus == MI_OK) 
    {
        /* 4.1 提取UID和校验字节 */
        for (uc = 0; uc < 4; uc++) 
        {
            pSnr[uc] = ucComMF522Buf[uc];  // 存储UID字节（4字节）
            ucSnr_check ^= ucComMF522Buf[uc]; // 计算异或校验
        }

        /* 4.2 验证BCC校验（第5字节应为前4字节的异或值） */
        if (ucSnr_check != ucComMF522Buf[4]) 
            cStatus = MI_ERR;  // 校验失败
    }

    /*── 5. 恢复冲突检测状态 ──*/
    SetBitMask(CollReg, 0x80);  // 允许后续冲突检测

    return cStatus;
}

/**
  ******************************************************************************
  * @brief  计算 RC522 硬件 CRC 校验值
  * @param  pIndata: 输入数据缓冲区
  * @param  ucLen:  输入数据长度（1-64字节）
  * @param  pOutData: CRC结果缓冲区（2字节，小端格式）
  * @retval None
  * @note   执行流程：
  *         1. 初始化CRC协处理器
  *         2. 写入待计算数据到FIFO
  *         3. 启动CRC计算
  *         4. 等待计算完成
  *         5. 读取CRC结果
  *         技术特性：
  *         - 使用ISO14443A标准的CRC-A算法（多项式：0x8408）
  *         - 计算时间：每字节约32us @13.56MHz
  *         - 最大支持64字节数据块
  ******************************************************************************
  */
void CalulateCRC(u8 *pIndata, u8 ucLen, u8 *pOutData)
{
    u8 uc, ucN;

    /*── 1. 初始化CRC计算器 ──*/
    ClearBitMask(DivIrqReg, 0x04);		// 清除DivIrqReg的bit2(CRCIRq)
    WriteRawRC(CommandReg, PCD_IDLE); 	// 停止当前命令
    SetBitMask(FIFOLevelReg, 0x80);  	// 清空FIFO缓冲区

    /*── 2. 写入待计算数据 ──*/
    for (uc = 0; uc < ucLen; uc++)
        WriteRawRC(FIFODataReg, *(pIndata + uc)); // 逐字节写入FIFO

    /*── 3. 启动CRC计算 ──*/
    WriteRawRC(CommandReg, PCD_CALCCRC); // 发送CRC计算命令

    /*── 4. 等待计算完成 ──*/
    uc = 0xFF; // 超时计数器（约5ms）
    do {
        ucN = ReadRawRC(DivIrqReg); // 读取DivIrqReg状态
        uc--;
    } while ((uc != 0) && !(ucN & 0x04)); // 等待CRCIRq标志置位

    /*── 5. 读取CRC结果（LSB first）──*/
    pOutData[0] = ReadRawRC(CRCResultRegL); // CRC低字节
    pOutData[1] = ReadRawRC(CRCResultRegM); // CRC高字节
}

/**
  ******************************************************************************
  * @brief  选择指定UID的卡片（ISO14443-3选择阶段）
  * @param  pSnr: 待选择卡片的UID（4字节）
  * @retval 操作状态：
  *         MI_OK  - 选择成功
  *         MI_ERR - 选择失败
  * @note   执行流程：
  *         1. 构建SELECT指令（包含UID和BCC）
  *         2. 计算CRC-A校验
  *         3. 发送选择命令
  *         4. 验证SAK响应
  *         协议规范：
  *         - 符合ISO14443-3选择流程
  *         - 必须在前置防冲突成功后调用
  *         电气特性：
  *         - 典型选择时间：1.2ms @13.56MHz
  ******************************************************************************
  */
char PcdSelect(u8 *pSnr)
{
    char ucN;
    u8 uc;
    u8 ucComMF522Buf[MAXRLEN];  // 指令缓冲区（MAXRLEN=18）
    u32 ulLen;                  // 响应数据位数
    
    /*── 1. 构建SELECT指令 ──*/
    ucComMF522Buf[0] = 0x93;     // SELECT命令码（CL1）
    ucComMF522Buf[1] = 0x70;     // NVB字节（参数见下表）
    ucComMF522Buf[6] = 0;        // 初始化BCC校验位
    
    /* 填充UID和计算BCC */
    for (uc = 0; uc < 4; uc++) {
        ucComMF522Buf[uc + 2] = *(pSnr + uc);  // UID字节（偏移2-5）
        ucComMF522Buf[6] ^= *(pSnr + uc);      // BCC = UID0^UID1^UID2^UID3
    }
    
    /*── 2. 计算CRC-A校验 ──*/
    CalulateCRC(ucComMF522Buf, 7, &ucComMF522Buf[7]); // CRC填充到偏移7-8
    
    /*── 3. 发送选择命令 ──*/
    ClearBitMask(Status2Reg, 0x08);  // 清除加密状态
    ucN = PcdComMF522(PCD_TRANSCEIVE, ucComMF522Buf, 9, ucComMF522Buf, &ulLen);
    
    /*── 4. 验证SAK响应 ──*/
    if ((ucN == MI_OK) && (ulLen == 0x18))  // 24位响应（3字节）
        ucN = MI_OK;
    else
        ucN = MI_ERR;
    
    return ucN;
}

/**
  ******************************************************************************
  * @brief  执行MIFARE卡片认证（Authentication）
  * @param  ucAuth_mode: 认证模式
  *          - 0x60: 密钥A认证
  *          - 0x61: 密钥B认证
  * @param  ucAddr:    要认证的块地址（0-63）
  * @param  pKey:      6字节密钥缓冲区
  * @param  pSnr:      卡片UID（4字节）+BCC（2字节）
  * @retval 认证状态：
  *         MI_OK  - 认证成功
  *         MI_ERR - 认证失败
  * @note   执行流程：
  *         1. 构建认证指令包
  *         2. 发送认证命令
  *         3. 验证状态寄存器
  *         安全特性：
  *         - 采用三次握手认证协议
  *         - 认证成功后自动加密后续通信
  *         典型用时：
  *         - 约4.8ms @13.56MHz
  ******************************************************************************
  */
char PcdAuthState(u8 ucAuth_mode, u8 ucAddr, u8 *pKey, u8 *pSnr)
{
    char cStatus;
    u8 uc;
    u8 ucComMF522Buf[MAXRLEN];  // 指令缓冲区
    u32 ulLen;                  // 响应数据长度（本命令无返回数据）

    /*── 1. 构建认证指令包 ──*/
    ucComMF522Buf[0] = ucAuth_mode;  // 认证模式（0x60/0x61）
    ucComMF522Buf[1] = ucAddr;       // 块地址
    
    // 填充6字节密钥
    for (uc = 0; uc < 6; uc++)
        ucComMF522Buf[uc + 2] = *(pKey + uc);
    
    // 填充卡片UID（4字节）+BCC（2字节）
    for (uc = 0; uc < 6; uc++)
        ucComMF522Buf[uc + 8] = *(pSnr + uc);

    /*── 2. 发送认证命令 ──*/
    cStatus = PcdComMF522(PCD_AUTHENT, ucComMF522Buf, 12, 
                         ucComMF522Buf, &ulLen);

    /*── 3. 验证认证结果 ──*/
    if ((cStatus != MI_OK) || (!(ReadRawRC(Status2Reg) & 0x08))) 
    {
        /* 失败场景：
         * - PCD_AUTHENT返回错误
         * - Status2Reg的MFCrypto1On位未置位
         */
        cStatus = MI_ERR;
    }
    
    return cStatus;
}

/**
  ******************************************************************************
  * @brief  向MIFARE卡片写入数据块
  * @param  ucAddr: 块地址（0-63）
  * @param  pData:  16字节数据缓冲区
  * @retval 操作状态：
  *         MI_OK  - 写入成功
  *         MI_ERR - 写入失败
  * @note   执行流程：
  *         1. 发送写命令+块地址
  *         2. 接收卡片确认
  *         3. 发送16字节数据
  *         4. 验证最终确认
  *         安全要求：
  *         - 目标块必须已通过PcdAuthState认证
  *         - 禁止直接写扇区尾块（块3/7/11...）
  *         典型用时：
  *         - 约5.8ms @13.56MHz
  ******************************************************************************
  */
char PcdWrite(u8 ucAddr, u8 *pData)
{
    char cStatus;
    u8 uc;
    u8 ucComMF522Buf[MAXRLEN];  // 指令缓冲区
    u32 ulLen;                  // 响应数据长度

    /*── 阶段1：发送写命令 ──*/
    ucComMF522Buf[0] = 0xA0;     // 写命令码（PICC_WRITE）
    ucComMF522Buf[1] = ucAddr;   // 块地址
    
    // 计算CRC并填充到缓冲区
    CalulateCRC(ucComMF522Buf, 2, &ucComMF522Buf[2]);
    
    // 发送命令（4字节：命令+地址+CRC）
    cStatus = PcdComMF522(PCD_TRANSCEIVE, ucComMF522Buf, 4, 
                         ucComMF522Buf, &ulLen);
    
    /*── 验证阶段1响应 ──*/
    if ((cStatus != MI_OK) ||          // 通信失败
        (ulLen != 4) ||                // 响应长度应为4位
        ((ucComMF522Buf[0] & 0x0F) != 0x0A)) // 确认码应为0x0A
    {
        return MI_ERR;
    }

    /*── 阶段2：发送数据块 ──*/
    // 填充16字节数据
    for (uc = 0; uc < 16; uc++)
        ucComMF522Buf[uc] = *(pData + uc);
    
    // 计算数据CRC（追加2字节）
    CalulateCRC(ucComMF522Buf, 16, &ucComMF522Buf[16]);
    
    // 发送数据包（18字节：16数据+2CRC）
    cStatus = PcdComMF522(PCD_TRANSCEIVE, ucComMF522Buf, 18,
                         ucComMF522Buf, &ulLen);
    
    /*── 验证阶段2响应 ──*/
    if ((cStatus != MI_OK) ||          // 通信失败
        (ulLen != 4) ||                // 响应长度应为4位
        ((ucComMF522Buf[0] & 0x0F) != 0x0A)) // 确认码应为0x0A
    {
        cStatus = MI_ERR;
    }
    
    return cStatus;
}

/**
  ******************************************************************************
  * @brief  从MIFARE卡片读取数据块
  * @param  ucAddr: 块地址（0-63）
  * @param  pData:  16字节数据接收缓冲区
  * @retval 操作状态：
  *         MI_OK  - 读取成功
  *         MI_ERR - 读取失败
  * @note   执行流程：
  *         1. 发送读命令+块地址
  *         2. 接收16字节数据
  *         安全要求：
  *         - 目标块必须已通过PcdAuthState认证（密钥A/B）
  *         - 禁止读取未认证的加密块
  *         典型用时：
  *         - 约2.4ms @13.56MHz
  ******************************************************************************
  */
char PcdRead(u8 ucAddr, u8 *pData)
{
    char cStatus;
    u8 uc;
    u8 ucComMF522Buf[MAXRLEN];  // 指令缓冲区
    u32 ulLen;                  // 响应数据长度（bit单位）

    /*── 1. 构建读指令包 ──*/
    ucComMF522Buf[0] = 0x30;     // 读命令码（PICC_READ）
    ucComMF522Buf[1] = ucAddr;   // 块地址
    
    // 计算CRC并填充到缓冲区
    CalulateCRC(ucComMF522Buf, 2, &ucComMF522Buf[2]);

    /*── 2. 发送读命令 ──*/
    cStatus = PcdComMF522(PCD_TRANSCEIVE, ucComMF522Buf, 4, 
                         ucComMF522Buf, &ulLen);

    /*── 3. 处理响应数据 ──*/
    if ((cStatus == MI_OK) && (ulLen == 0x90))  // 144bit=18字节（16数据+2CRC）
    {
        // 拷贝有效数据（跳过CRC）
        for (uc = 0; uc < 16; uc++)
            *(pData + uc) = ucComMF522Buf[uc];
    }
    else
    {
        cStatus = MI_ERR;
        /* 调试时可添加：
         * USART1_Printf("Read failed: status=%d len=%lu", cStatus, ulLen);
         */
    }

    return cStatus;
}

/**
  ******************************************************************************
  * @brief  发送休眠命令使卡片进入休眠状态（HALT）
  * @param  None
  * @retval 操作状态（固定返回MI_OK）
  * @note   执行流程：
  *         1. 构建HALT指令包（0x50 + 0x00 + CRC）
  *         2. 发送指令到卡片
  *         3. 无需等待响应直接返回
  *         协议特性：
  *         - 符合ISO14443-3的HALT命令规范
  *         - 卡片将保持休眠直到收到新的REQA/WUPA
  *         硬件控制：
  *         - 自动关闭射频场以降低功耗
  *         典型用时：
  *         - 约320μs @13.56MHz
  ******************************************************************************
  */
char PcdHalt(void)
{
    u8 ucComMF522Buf[MAXRLEN]; 
    u32 ulLen;

    /*── 1. 构建HALT指令包 ──*/
    ucComMF522Buf[0] = 0x50;     // HALT命令码（PICC_HALT）
    ucComMF522Buf[1] = 0x00;     // 填充字节（必须为0）
    
    /*── 2. 计算CRC并填充 ──*/
    CalulateCRC(ucComMF522Buf, 2, &ucComMF522Buf[2]);

    /*── 3. 发送指令（不检查响应）──*/
    PcdComMF522(PCD_TRANSCEIVE, ucComMF522Buf, 4, 
               ucComMF522Buf, &ulLen);

    /*── 4. 建议的硬件优化操作 ──*/
    PcdAntennaOff();  // 关闭射频场以节能
    Delay_us(500);    // 等待场强完全衰减

    return MI_OK;  // 固定返回成功（卡片无响应）
}

/**
  ******************************************************************************
  * @brief  MIFARE Classic卡片读写主控函数
  * @param  UID:  卡片UID输入/输出缓冲区（4字节）
  * @param  KEY:  6字节密钥缓冲区
  * @param  RW:   读写模式选择
  *               - 1: 读取块数据
  *               - 0: 写入块数据
  * @param  Dat:  数据缓冲区（16字节）
  * @retval None
  * @note   执行流程：
  *         1. 卡片唤醒与识别（REQA+ANTICOLL）
  *         2. 选择卡片（SELECT）
  *         3. 块认证（AUTH）
  *         4. 数据读写操作
  *         5. 休眠卡片（HALT）
  *         安全特性：
  *         - 固定操作块地址0x10（可根据需求修改）
  *         - 自动处理完整ISO14443-3流程
  *         典型用时：
  *         - 读操作：约12ms
  *         - 写操作：约18ms @13.56MHz
  ******************************************************************************
  */
void IC_CMT(u8 *UID, u8 *KEY, u8 RW, u8 *Dat)
{
    u8 ucArray_ID[4] = {0};  // 卡片识别缓冲区

    /*── 1. 卡片唤醒与识别 ──*/
    PcdRequest(0x52, ucArray_ID);  // 发送WUPA唤醒（0x52）
    PcdAnticoll(ucArray_ID);       // 防冲突获取UID
    memcpy(UID, ucArray_ID, 4);    // 保存UID到输出缓冲区

    /*── 2. 选择卡片 ──*/
    PcdSelect(UID);  // 执行SELECT流程

    /*── 3. 块认证 ──*/
    PcdAuthState(0x60, 0x10, KEY, UID);  // 密钥A认证块0x10

    /*── 4. 数据操作 ──*/
    if (RW)  // 读模式
        PcdRead(0x10, Dat);
    else     // 写模式
        PcdWrite(0x10, Dat);

    /*── 5. 休眠卡片 ──*/
    PcdHalt();  // 发送HALT命令
}

/**
  ******************************************************************************
  * @brief  显示卡片UID（十六进制格式）
  * @param  x: 显示起始X坐标（像素）
  * @param  y: 显示起始Y坐标（像素）
  * @param  p: 卡片UID缓冲区（4字节）
  * @param  charColor: 字符颜色（RGB565格式）
  * @param  bkColor: 背景颜色（RGB565格式）
  * @retval None
  * @note   功能特性：
  *         - 支持标准4字节UID显示
  *         - 自动转换为大写十六进制格式
  *         - 兼容LCD显示和串口打印
  *         显示示例：
  *         ID>>> 01 A3 FF 2C
  ******************************************************************************
  */
void ShowID(u16 x, u16 y, u8 *p, u16 charColor, u16 bkColor)
{
    u8 num[12];  // 缓冲区："XX XX XX XX\0"
    u8 i;

    /*── 1. 格式转换 ──*/
    for(i=0; i<4; i++)
    {
        // 高四位转换
        num[i*3] = (p[i] >> 4) & 0x0F;
        num[i*3] += (num[i*3] < 10) ? '0' : ('A'-10);
        
        // 低四位转换
        num[i*3+1] = p[i] & 0x0F;
        num[i*3+1] += (num[i*3+1] < 10) ? '0' : ('A'-10);
        
        // 添加空格分隔符（最后字节不加）
        num[i*3+2] = (i<3) ? ' ' : '\0';
    }

    /*── 2. 显示输出 ──*/
#if defined(USE_LCD)
    LCD_ShowString(x, y, 110, 16, 16, num);  // 参数说明：x,y,宽度,高度,字体大小,字符串
#else
    USART1_Printf("ID>>> %s\r\n", num);  // 串口输出示例：ID>>> 01 A3 FF 2C
#endif
}
